package com.learnyou.server.xingzuo.biz.common.impl;

import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.stereotype.Service;
import com.learnyou.server.xingzuo.common.exception.LearnyouBootException;
import com.learnyou.server.xingzuo.biz.StorageBiz;
import com.learnyou.server.xingzuo.biz.client.WechatClient;
import com.learnyou.server.xingzuo.biz.common.*;
import com.learnyou.server.xingzuo.biz.handle.XingzuoCacheHandler;
import com.learnyou.server.xingzuo.biz.helper.CosHelper;
import com.learnyou.server.xingzuo.biz.helper.WechatClientBizHelper;
import com.learnyou.server.xingzuo.common.config.WechatConfig;
import com.learnyou.server.xingzuo.common.config.WechatTemplateConfig;
import com.learnyou.server.xingzuo.common.config.WechatThirdConfig;
import com.learnyou.server.xingzuo.common.constants.ErrorCode;
import com.learnyou.server.xingzuo.common.wechat.aes.WXBizMsgCrypt;
import com.learnyou.server.xingzuo.dto.paramDTO.wechat.*;
import com.learnyou.server.xingzuo.dto.paramDTO.wechat.template.NormalTemplateMsgDataDTO;
import com.learnyou.server.xingzuo.dto.paramDTO.wechat.third.*;
import com.learnyou.server.xingzuo.dto.paramDTO.wechat.template.AttendanceMsgDataDTO;
import com.learnyou.server.xingzuo.dto.resultDTO.wechat.WechatNormalResultDTO;
import com.learnyou.server.xingzuo.dto.resultDTO.wechat.WechatWxaUrlLinkGenResultDTO;
import com.learnyou.server.xingzuo.dto.wechat.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class WechatClientBizImpl extends AbstractWechatClientBiz {
    @Resource
    private WechatClient wechatClient;

    @Resource
    private WechatConfig wechatConfig;

    @Resource
    private WechatTemplateConfig templateConfig;

    @Resource
    private WechatThirdConfig wechatThirdConfig;

    @Resource
    private CacheManager cacheManager;

//    @Resource
//    private OrgBiz orgBiz;

    @Resource
    private CosFileBiz fileBiz;

    @Resource
    private StorageBiz storageBiz;

    @Override
    public String getToken(){
        return getTokenOf(CacheTokenEnum.MINI_APP_TOKEN, null);
    }

    @Override
    public String getThirdToken() {
        return getTokenOf(CacheTokenEnum.THIRD_TOKEN, null);
    }

    @Override
    public String getPreAuthCode() {
        String token = getThirdToken();
        WechatThirdPreAuthParamDTO authParamDTO = WechatThirdPreAuthParamDTO.builder()
                .appId(wechatThirdConfig.getAppId())
                .build();

        WechatThirdPreAuthCodeResultDTO thirdPreAuthCodeResultDTO = null;
        try {
            thirdPreAuthCodeResultDTO = wechatClient.getPreAuthCode(token,authParamDTO);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_PRE_AUTH_CODE_FAILED);
        }

        if (Objects.nonNull(thirdPreAuthCodeResultDTO) && (ObjectUtils.isEmpty(thirdPreAuthCodeResultDTO.getErrcode()) || thirdPreAuthCodeResultDTO.getErrcode().equals("0"))) {
            return thirdPreAuthCodeResultDTO.getPre_auth_code();
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_PRE_AUTH_CODE_FAILED);
        }
    }

    @Override
    public WechatThirdAuthInfo getAuthInfo(String authCode) {
        String thirdToken = getThirdToken();

        WechatThirdAuthResultDTO thirdAuthResultDTO;
        try {
            WechatThirdApiQueryAuthParamDTO apiQueryAuthParamDTO = WechatThirdApiQueryAuthParamDTO.builder()
                    .component_appid(wechatThirdConfig.getAppId())
                    .authorization_code(authCode)
                    .build();
            thirdAuthResultDTO = wechatClient.getApiQueryAuth(thirdToken, apiQueryAuthParamDTO);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTH_INFO_FAILED);
        }

        if (Objects.nonNull(thirdAuthResultDTO) && (ObjectUtils.isEmpty(thirdAuthResultDTO.getErrcode()) || thirdAuthResultDTO.getErrcode().equals("0"))) {
            return thirdAuthResultDTO.getAuthorization_info();
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTH_INFO_FAILED);
        }
    }

    @Override
    public WechatThirdAuthorizerResultDTO getAuthorizerInfo(WechatThirdAuthInfo authInfo) {
//        WechatThirdAuthInfo authInfo = this.getAuthInfo(authCode);

        log.info("WechatThirdAuthInfo: {}", authInfo);

        String thirdToken = getThirdToken();
        String appId = authInfo.getAuthorizer_appid();
        WechatThirdAuthorizerResultDTO thirdAuthorizerResult = null;
        try {

            WechatThirdAuthorizerInfoQueryParamDTO authorizerInfoQueryParamDTO = WechatThirdAuthorizerInfoQueryParamDTO.builder()
                    .component_appid(wechatThirdConfig.getAppId())
                    .authorizer_appid(appId)
                    .build();
            thirdAuthorizerResult = wechatClient.getAuthorizerInfo(thirdToken, authorizerInfoQueryParamDTO);
            thirdAuthorizerResult.setAuthorization_info(authInfo);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTHORIZER_INFO_FAILED);
        }

        if (Objects.nonNull(thirdAuthorizerResult) && (ObjectUtils.isEmpty(thirdAuthorizerResult.getErrcode()) || thirdAuthorizerResult.getErrcode().equals("0"))) {

            // 放入存储中
            WechatThirdAuthorizerTokenResultDTO authorizerToken = WechatThirdAuthorizerTokenResultDTO.builder()
                    .authorizer_access_token(authInfo.getAuthorizer_access_token())
                    .authorizer_appid(appId)
                    .authorizer_refresh_token(authInfo.getAuthorizer_refresh_token())
                    .created_at(System.currentTimeMillis() / 1000)
                    .expires_in(authInfo.getExpires_in())
                    .build();
            // 更新缓存
            getCache().put(appId, authorizerToken);

            // 持久化
            int saved = storageBiz.upsertStorage(appId, authorizerToken);
            if (saved > 0) {
                log.info("storage info of App: {}, value: {}", authInfo.getAuthorizer_appid(), authorizerToken);
            } else {
                log.warn("storage failed: appId: {}", authInfo.getAuthorizer_appid());
            }

            return thirdAuthorizerResult;
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTHORIZER_INFO_FAILED);
        }
    }

    @Override
    public String getThirdUserOpenId(String code, String appId) {
        WechatThirdPrivateAuthTokenParamDTO paramDTO = WechatThirdPrivateAuthTokenParamDTO.builder()
                .code(code)
                .grant_type("authorization_code")
                .component_appid(wechatThirdConfig.getAppId())
                .component_access_token(getThirdToken())
                .appid(appId)
                .build();
        WechatThirdPrivateAuthTokenResultDTO privateAuthTokenResultDTO;
        log.info("code: {}, appId: {}", code, appId);
        try {
            privateAuthTokenResultDTO = wechatClient.getThirdPrivateAuthToken(paramDTO);
        }  catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTHORIZER_INFO_FAILED);
        }

        if (Objects.nonNull(privateAuthTokenResultDTO) && (ObjectUtils.isEmpty(privateAuthTokenResultDTO.getErrcode()) || privateAuthTokenResultDTO.getErrcode().equals("0"))) {
            log.info("privateAuthTokenResultDTO: {}", privateAuthTokenResultDTO);
            return privateAuthTokenResultDTO.getOpenid();
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_GET_AUTHORIZER_INFO_FAILED);
        }

    }

    @Override
    public String generateWxaUrlLink(String query) {
        WechatWxaUrlLinkGenResultDTO urlLinkGenResultDTO = null;
        String token = this.getToken();
        try {
            WechatWxaUrlLinkGenParamDTO paramDTO = WechatWxaUrlLinkGenParamDTO.builder()
                    .path("/pages/index/index")
                    .query(query)
                    .build();
            urlLinkGenResultDTO = wechatClient.generateWxaUrlLink(token, paramDTO);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_GEN_URL_LINK_FAILED);
        }

        if (Objects.nonNull(urlLinkGenResultDTO) && (ObjectUtils.isEmpty(urlLinkGenResultDTO.getErrcode()) || urlLinkGenResultDTO.getErrcode().equals("0"))) {
            log.info("urlLinkGenResultDTO: {}", urlLinkGenResultDTO);
            return urlLinkGenResultDTO.getUrl_link();
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_GEN_URL_LINK_FAILED);
        }
    }

    @Override
    public String getAuthorizerToken(String appId) {
        return getTokenOf(CacheTokenEnum.THIRD_AUTHORIZER_TOKEN, appId);
    }

    @Override
    public int getMPIndustrySettingStatus(String appId) {
        int isValid = -1;
        if (!ObjectUtils.isEmpty(appId)) {
            String token = this.getAuthorizerToken(appId);
            WechatIndustryResultDTO industryResultDTO = wechatClient.getIndustry(token);
            if (Objects.nonNull(industryResultDTO) && (ObjectUtils.isEmpty(industryResultDTO.getErrcode()) || industryResultDTO.getErrcode().equals("0"))) {
                isValid = WechatClientBizHelper.isIndustryValid(industryResultDTO) ? 0: 1;
            }
        }
        return isValid;
    }

    @Override
    public int getMPTemplateSettingStatus(String appId) {
        int isValid = -1;
        if (!ObjectUtils.isEmpty(appId)) {
            String token = getAuthorizerToken(appId);
            WechatTemplateResultDTO templateResultDTO = wechatClient.getAllPrivateTemplate(token);
            if (Objects.nonNull(templateResultDTO) && (ObjectUtils.isEmpty(templateResultDTO.getErrcode()) || templateResultDTO.getErrcode().equals("0"))) {
                isValid = WechatClientBizHelper.isTemplateSettingValid(templateResultDTO) ? 0 : 1;
            }
        }
        return isValid;
    }

    @Override
    public boolean isContentValid(String openId, String content) {
        boolean isMedia = CosHelper.isUrl(content);
        String token = this.getToken();
        WechatContentSecurityCheckResultDTO securityCheckResult = null;
        if (isMedia) {

            WechatMediaSecurityCheckParamDTO mediaSecurityCheckParamDTO = WechatMediaSecurityCheckParamDTO.builder()
                    .media_url(content)
                    .media_type(2) // 1:音频;2:图片
                    .openid(openId)
                    .scene(1) // 场景枚举值（1 资料；2 评论；3 论坛；4 社交日志）
                    .version(2) // 固定值
                    .build();
            securityCheckResult = wechatClient.mediaSecurityCheck(token, mediaSecurityCheckParamDTO);
        } else {
            WechatTextSecurityCheckParamDTO paramDTO = WechatTextSecurityCheckParamDTO.builder()
                    .content(content)
                    .openid(openId)
                    .version(2)
                    .scene(1)
                    .build();
            securityCheckResult = wechatClient.msgSecurityCheck(token, paramDTO);
        }

        log.info("securityCheckResult: {}", securityCheckResult);
        // 内容检查接口是否发送成功
        boolean isRequestSuccess = ObjectUtils.isEmpty(securityCheckResult.getErrcode()) || String.valueOf(securityCheckResult.getErrcode()).equals("0");

        if (Objects.nonNull(securityCheckResult) && isRequestSuccess) {

            if (isMedia) {
                return true;
            } else {
                // 非媒体检查（文本检查为实时获取结果）
                WechatSecurityCheckItem checkResult = securityCheckResult.getResult();
                if(Objects.nonNull(checkResult) && checkResult.getLabel().equals(100)) {
                    return true;
                } else {
                    return false;
                }
            }
        } else {
            // 内容检查接口是否发送失败
            return false;
        }
    }

    @Override
    public String getTemplateId(String appId, MPMessageType mpMessageType) {
        String token = getAuthorizerToken(appId);
        WechatTemplateResultDTO templateResultDTO = wechatClient.getAllPrivateTemplate(token);

        String templateId = null;
        if (Objects.nonNull(templateResultDTO) &&
                (ObjectUtils.isEmpty(templateResultDTO.getErrcode()) || templateResultDTO.getErrcode().equals("0")) &&
                CollUtil.isNotEmpty(templateResultDTO.getTemplate_list())
        ) {
            List<WechatTemplateItem> templateItemList = templateResultDTO.getTemplate_list();
            // 查找指定类型的模板
            WechatTemplateItem templateItem = templateItemList.stream().filter(item-> isTemplateItemMatch(item, mpMessageType))
                    .findFirst().orElse(null);

            templateId = Objects.nonNull(templateItem) ? templateItem.getTemplate_id() : null;
        }
        return templateId;
    }

    /**
     * 模板是否是指定的类型
     * @param templateItem
     * @param mpMessageType
     * @return
     */
    private static boolean isTemplateItemMatch(WechatTemplateItem templateItem, MPMessageType mpMessageType) {
        return templateItem.getTitle().equalsIgnoreCase(mpMessageType.getTemplateName()) &&
                templateItem.getPrimary_industry().equalsIgnoreCase(mpMessageType.getPrimaryIndustry()) &&
                templateItem.getDeputy_industry().equalsIgnoreCase(mpMessageType.getSubIndustry());
    }

    @Override
    public String getOpenId(String code) {
        WechatLoginResultDataDTO resp = null;
        try {
            resp = wechatClient.login(wechatConfig.getAppId(), wechatConfig.getSecretKey(), code);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_OPENID_FAILED);
        }

        if (Objects.nonNull(resp) && (ObjectUtils.isEmpty(resp.getErrcode()) || resp.getErrcode().equals("0"))) {
            return resp.getOpenid();
        } else {
            throw new LearnyouBootException(ErrorCode.WECHAT_OPENID_FAILED);
        }
    }

    @Override
    public String phoneNumber(String phoneCode){
        WechatPhoneInfoDTO phoneInfo = null;

        WechatPhoneRequestBody requestBody = WechatPhoneRequestBody.builder()
                .code(phoneCode)
                .build();
        try {
            String token = this.getToken();
            phoneInfo = wechatClient.phoneNumber(token, requestBody);
        } catch (Exception ex) {
            log.warn("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_PHONE_FAILED);
        }

        if (Objects.nonNull(phoneInfo) && (ObjectUtils.isEmpty(phoneInfo.getErrcode()) || phoneInfo.getErrcode().equals("0"))) {
            return phoneInfo.getPhone_info().getPurePhoneNumber();
        } else {
            if (Objects.nonNull(phoneInfo)) {
                log.warn("get phoneNumber error: {}", phoneInfo);
            } else {
                log.warn("get phoneNumber request error.");
            }
            throw new LearnyouBootException(ErrorCode.WECHAT_PHONE_FAILED);
        }
    }

//    @Override
//    public boolean sendAttendanceMsg(String targetOpenId, AttendanceMsgDataDTO attendanceMsgDataDTO) {
//        return sendUniformMsg(targetOpenId, attendanceMsgDataDTO, templateConfig.getAttendance(), wechatConfig);
//    }
//
//    @Override
//    public boolean sendCommentMsg(String wxId, AttendanceMsgDataDTO attendanceMsgDataDTO) {
//        return sendUniformMsg(wxId, attendanceMsgDataDTO, templateConfig.getComment(), wechatConfig);
//    }
//
//    @Override
//    public boolean sendCourseRemindMsg(String wxId, AttendanceMsgDataDTO attendanceMsgDataDTO) {
//        return sendUniformMsg(wxId, attendanceMsgDataDTO, templateConfig.getCourseReminder(), wechatConfig);
//    }

    @Override
    public boolean sendNormalTemplateMsg(String appId, String toUser, String templateId, NormalTemplateMsgDataDTO normalTemplateMsgDataDTO) {
        boolean success = false;
        WechatNormalTemplateMsgParamDTO<NormalTemplateMsgDataDTO> templateMsgParamDTO = WechatNormalTemplateMsgParamDTO.<NormalTemplateMsgDataDTO>builder()
                .template_id(templateId)
                .touser(toUser)
                .data(normalTemplateMsgDataDTO)
//                .url("http://weixin.qq.com/download")
                .build();
        try {
            if (Objects.nonNull(appId)) {
                String token = getTokenOf(CacheTokenEnum.THIRD_AUTHORIZER_TOKEN, appId);
                if (Objects.nonNull(token)) {
                    // 发送模板消息

                    WechatNormalResultDTO normalResultDTO = wechatClient.sendTemplateMessage(token, templateMsgParamDTO);
                    log.info("发送模板消息响应 WechatNormalResultDTO: {}", normalResultDTO);
                    if (Objects.nonNull(normalResultDTO) && (ObjectUtils.isEmpty(normalResultDTO.getErrcode()) || normalResultDTO.getErrcode().equals("0"))) {
                        success = true;
                    }
                }
            } else {
                log.warn("暂未进行公众号授权,AppID: {}", appId);
            }
        } catch (Exception ex) {
            log.warn("error:", ex);
            log.warn("授权公众号模板消息发送失败,AppID: {}", appId);
        }

        return success;
    }

    @Override
    public WechatThirdAuthTicketDTO parseTicketFromPush(String msgSignature, String timestamp, String nonce, String postData) {
        try {
            WXBizMsgCrypt pc = new WXBizMsgCrypt(wechatThirdConfig.getComponentToken(), wechatThirdConfig.getAesKey(), wechatThirdConfig.getAppId());
            String xml = pc.decryptMsg(msgSignature, timestamp, nonce, postData);

            ObjectMapper xmlMapper = new XmlMapper();
            WechatThirdAuthTicketDTO authPushData = xmlMapper.readValue(xml, WechatThirdAuthTicketDTO.class);

            // 保存票据
            this.updateTicketStorage(authPushData.getVerifyTicket());

            return authPushData;
        } catch (Exception ex) {
            log.error("error:", ex);
            throw new LearnyouBootException(ErrorCode.WECHAT_THIRD_TICKET_PARSE_FAILED);
        }
    }

    @Override
    public String getThirdTicket() {
        String ticket = storageBiz.getStorageValue(XingzuoConstants.CACHE_TICKET_KEY);
        if (Objects.nonNull(ticket)) {
            log.info("ticket is: {}", ticket);
            return ticket;
        } else {
            log.warn("ticket is NULL. wait for moment.");
            return null;
        }
    }

    @Override
    public Cache getCache() {
        return cacheManager.getCache(XingzuoCacheHandler.WECHAT_TOKEN_CACHE);
    }

//    @Override
//    public WechatNormalResultDTO sendUniformMessage(String targetOpenId, WechatUniformTemplateMsgDTO attendanceMsgDataDTO) {
//        return wechatClient.sendUniformMessage(targetOpenId, attendanceMsgDataDTO);
//    }

//    @Override
//    public WechatNormalResultDTO sendNormalTemplateMessageWithClient(String token, WechatNormalTemplateMsgParamDTO templateMsgParamDTO) {
//        return wechatClient.sendTemplateMessage(token, templateMsgParamDTO);
//    }

    @Override
    public Object refreshToken(CacheTokenEnum tokenEnum, Object oldValue) {
        Object tokenResult = null;
        switch (tokenEnum) {
            case MINI_APP_TOKEN:
                WechatTokenDataDTO newTokenData = wechatClient.token(wechatConfig.getAppId(), wechatConfig.getSecretKey());
                newTokenData.setCreated_at(System.currentTimeMillis() / 1000);
                tokenResult = newTokenData;
                break;
            case THIRD_TOKEN:
                WechatThirdTokenParamDTO paramDTO = WechatThirdTokenParamDTO.builder()
                        .appId(wechatThirdConfig.getAppId())
                        .appSecret(wechatThirdConfig.getSecret())
                        .ticket(getThirdTicket())
                        .build();
                WechatThirdTokenDataDTO newThirdToken = wechatClient.thirdToken(paramDTO);
                newThirdToken.setCreated_at(System.currentTimeMillis() / 1000);
                tokenResult = newThirdToken;
                break;

            case THIRD_AUTHORIZER_TOKEN:
                if (Objects.nonNull(oldValue)) {
                    WechatThirdAuthorizerTokenResultDTO authorizerTokenResultDTO = (WechatThirdAuthorizerTokenResultDTO) oldValue;
                    WechatThirdAuthorizerTokenParamDTO authorizerTokenParam =
                            WechatThirdAuthorizerTokenParamDTO.builder()
                                    .component_appid(wechatThirdConfig.getAppId())
                                    .authorizer_appid(authorizerTokenResultDTO.getAuthorizer_appid())
                                    .authorizer_refresh_token(authorizerTokenResultDTO.getAuthorizer_refresh_token())
                                    .build();

                    WechatThirdAuthorizerTokenResultDTO newAuthorizerTokenResult = wechatClient.getAuthorizerAccessToken(getThirdToken(), authorizerTokenParam);
                    newAuthorizerTokenResult.setCreated_at(System.currentTimeMillis()/1000);
                    newAuthorizerTokenResult.setAuthorizer_appid(authorizerTokenResultDTO.getAuthorizer_appid());
                    tokenResult = newAuthorizerTokenResult;
                }
                break;

        }
        return tokenResult;
    }

    @Override
    public String getFromStorage(String key) {
        return storageBiz.getStorageValue(key);
    }


    @Override
    public int upsertStorage(String key, Object value) {
        return storageBiz.upsertStorage(key, value);
    }
}
